#include "clusters.h"
//#include <boost/foreach.hpp>

namespace Clustering
{
	void randomInit	(Points & ps, unsigned int dims, 
					unsigned int num_points)
	{
		for (unsigned int j = 0; j < num_points; j++)
		{
			Point p(dims);
			for (unsigned int i = 0; i < dims; i++)	
			{
				p(i) = (-1.0 + rand() * (2.0) / RAND_MAX);
	//			std::cout << p(i) << ' ';
			}
			ps.insert(&p);
	//		std::cout << std::endl;
		}
	}

	//will take as parameter all trajectories maybe a vector of TrajSet (each trajSet is one user)
	void getSetOfDatas(Points & ps, std::vector<TrajSet> data){
		try{
			int sizeVectData = data.size();
			for(int i=0; i< sizeVectData; i++){
		
				TrajSet trajS = data[i];
				int sizeTrajS = trajS.getSetOfTraj().size();

				cout << "size of sizeTrajS : " << sizeTrajS << endl;
				for(int j=0; j< sizeTrajS; j++){
			
					Trajectory traj = trajS.getSetOfTraj()[j];
					int sizeTraj = traj.getPoints().size();
					
					cout << "size of sizeTraj : " << sizeTraj << endl;
					if(sizeTraj < 5000){
						for(int k=0; k< sizeTraj; k++){
							Point* pC = new Point(4);
							PointTraj p = traj.getPoints()[k];

							/*
							get attributes this way if use Cosine distance
							pC(0) = 100000*p.getLatitude();
							pC(1) = 100000*p.getLongitude();
							pC(2) = 10000*p.getTimestamp();
							*/
							(*pC)(0) = p.getLatitude();
							(*pC)(1) = p.getLongitude();
							(*pC)(2) = p.getTimestamp();
							(*pC)(3) = trajS.getUserId();


							//cout << "size of ps : " << ps.size() << endl;
							//cout << "max size of ps : " << ps.max_size() << endl;
							ps.insert(pC);

						}
					}
				


				}



			}

			
		}catch(...){
		
			cout << "in get data" << endl;
		}

	}




	// assign each point to a new cluster
	void Clusters::uniformPartition()
	{
		PointId pid = 0;
		ClusterId cid = 0;
		//BOOST_FOREACH(Point p, _ps)
		Point* p = _ps.get_first();
		do{
			// create a new cluster for this current point
			Cluster *clust = new Cluster(cid);
			ClusterPoints* c = clust->getClusterRef();
			PointId *pidToInsert = new PointId(pid);
			pid++;
			c->insert(pidToInsert);
			_clusters.insert(clust);
			ClusterId *cidToInsert = new ClusterId(cid);
			cid++;
			_pointId_to_clusterId.insert(cidToInsert);	

			p = _ps.get_next();
		}while(p != _ps.get_last());
	}
	
	// findNeighbors(PointId pid, double threshold) 	
	
	Neighbors* Clusters::findNeighbors(Metrics::Distance<Metrics::MeterDistance<Clustering::Point>> & d, PointId pid, long double threshold, long double thresholdGeo, long double thresholdTime)
	{
		Neighbors* ne = new Neighbors();

		//ofstream out("sim_test_result.txt");
		//cout << "in find ne" << endl;

		Point pidPoint = *_ps.get(pid);
		int userIdPid = pidPoint[3];
		unsigned int sizeSim = _ps.get_num();
		for (unsigned int j=0; j < sizeSim; j++)
		{
			Point jPoint = *_ps.get(j);
			//if(userIdPid != jPoint[3]){
				long double simPidJ = d.similarity(pidPoint, jPoint, thresholdGeo);
	//				*_sim.get_element(pid, j);



				long double simTimePidJ = d.similarityTime(pidPoint, jPoint, thresholdTime);
	//				*_simTime.get_element(pid, j);

			
				if 	((pid != j ) && ( simPidJ > threshold && simTimePidJ > threshold))
				{
					PointId *val = new PointId(j);
				
					ne->insert(val);
				
					//std::cout << "sim(" << pid  << "," << j << ")" << _sim(pid, j) << ">" << threshold << std::endl;
				}
			//}
			
		}
		//cout << endl;
		//out.close();
		return ne;
	}




	void Clusters::createClusterCordinates(){
	
		//for(std::vector<Cluster>::iterator i = _clusters.begin(); i != _clusters.end(); i++){
		Cluster* currentCluster = _clusters.get_first();
		while(currentCluster != NULL){



			ClusterPoints *pts = (*currentCluster).getClusterRef();
			
			long double accLat = 0;
			long double accLong = 0;
			long double accTimeStamp = 0;


			//for(std::vector<PointId>::iterator j = pts.begin(); j != pts.end(); j++){
			PointId *currentPid = pts->get_first();
			while(currentPid != NULL){


				Point p = *_ps.get(*currentPid);

				accLat += p[0];
				accLong += p[1];
				accTimeStamp += p[2];

				currentPid = pts->get_next();

			}
			int nbPtsInCluster = pts->get_num();

			currentCluster->setAverageLat(accLat / nbPtsInCluster);
			currentCluster->setAverageLong(accLong / nbPtsInCluster);
			currentCluster->setAverageTimeStamp(accTimeStamp / nbPtsInCluster);

		
			currentCluster = _clusters.get_next();
		}
	
	}


	

	// single point output
	std::ostream& operator<<(std::ostream& o, const Point& p)
	{
		o << setprecision(16);
		o << "{ ";
		BOOST_FOREACH(Point::value_type x, p)
		{
			o << " " << x;
		}
		o << " }, \n";

		return o;
	}

	// single cluster output
	std::ostream& operator<<(std::ostream& o, const Clustering::Cluster& clust)
	{	
		Cluster clust1 = clust;
		ClusterPoints c = clust1.getCluster();
		o << "[ ";
		//BOOST_FOREACH(PointId pid, c)
		PointId *pid = c.get_first();
		while(pid != NULL){
			o << " " << pid;

			pid = c.get_next();
		}
		o << " ]";

		return o;
	}

	// clusters output
	std::ostream& operator<<(std::ostream& o, const Clusters& cs)
	{
		
		o << setprecision(16);
		//otherwise I cannot use "get_first" on const object
		Clusters* cs1 = new Clusters(cs);
		
		cout << "nbClusters : " << cs1->_clusters.get_num() << endl;
		
		

		Cluster *clust = cs1->_clusters.get_first();		
		while(clust != NULL){

			ClusterPoints* c = clust->getClusterRef();

			ClusterId *cid = new ClusterId(clust->getClusterId());

			o << "{" << endl;
			
			
			
			PointId* pid = c->get_first();

			Point *firstPoint = new Point(*cs1->_ps.get(*pid));
			//write the timestamp of the first one to get them sorted
			o << (*firstPoint)[2] << endl;

			while(pid != NULL){
				Point *p = new Point(*cs1->_ps.get(*pid));
				o << (*p)[0] << " " << (*p)[1] << " " << (*p)[3] << endl;
				pid = c->get_next();
			}
			o << "}" << endl ;

			clust = cs1->_clusters.get_next();
		}
		
		return o;
	}


};
